第5章 硬件辅助虚拟化

在CPU虚拟化方面,InteVT提供了VT-x(IntelVirtualizationtechnologyforx86)技术;在内存虚拟化方面,IntelVT提供了EPT(ExtendedPageTable)技术;在I/O设备虚拟化方面,IntelVT提供了VT-d(IntelVirtualizationTechnologyforDirectI/0) 等技术。

CPU虚拟化的硬件支持

VT-x引入了两种操作模式:

  1. VMX Root Operation:VMM运行所处的模式,简称根模式
  2. VMX Non-Root Operation: 客户机运行所处的模式,简称非根模式

这两种操作模式与特权级0~3是正交的,即在每种操作模式下都有相应的特权级0~3的特权级。

引人两种操作模式的理由很明显。我们知道指令的虚拟化是通过“陷人再模拟”的方式实现的,而IA32架构有19条敏感指令不能通过这种方法处理,导致了虚拟化漏洞。最直观的解决办法,是使得这些敏感指令能够触发异常。可惜这种方法会改变这些指令的语义,导致与原有软件不兼容,这是不可接受的。引人新的模式可以很好地解决问题。非根模式下所有敏感指令(包括19条不能被虚拟化的敏感指令)的行为都被重新定义,使得它们能不经虚拟化就直接运行或通过“陷人再模拟”的方式来处理;在根模式下,所有指令的行为和传统IA32一样,没有改变,因此原有的软件都能正常运行。

VT-x中,非根模式下敏感指令引起的“陷人”被称为VM-Exit.VM-Exit发生时,CPU自动从非根模式切换成为根模式。相应地,VT-x也定义了VM-Entry,该操作由VMM发起,通常是调度某个客户机运行,此时CPU从根模式切换成为非根模式。

VMCS

VMCS,virtual machine control structure. VMCS是保存在内存中的数据结构,包含了虚拟CPU的相关寄存器的内容和虚拟CPU相关的控制信息,每个VMCS对应一个虚拟CPU。VMCS与物理CPU也是一对一的绑定关系。

VT-x提供了两条指令用于VMCS的绑定与解除绑定:

VMCS的一次迁移工程如下:

  1. 在CPU1上执行VMCLEAR解除绑定
  2. 在CPU2上执行VMPTRLD进行绑定

VMCS主要信息存放在数据域。可以使用VMREAD <INDEX>VMWRITE <INDEX> <DATA>访问指定索引的数据域。

VMX操作模式

VMX的打开关闭指令:VMXON/VMXOFF

VMM与客户软件的交互流程如下:

  1. VMM执行VMXON指令进入VMX操作模式,CPU处于VMX根操作模式,VMM软件开始执行
  2. VMM执行VMLAUNCH或VMRESUME指令产生VM-Entry,客户机软件开始执行,此时CPU进入非根模式
  3. 当客户机执行特权指令,或者当客户机运行时发生了中断或异常,VM-Exit被触发而陷入倒VMM,CPU切换到根模式。VMM根据VM-Exit的原因做相应处理,然后转到2继续运行客户机
  4. 如果VMM决定退出,则执行VMXOFF关闭VMX操作模式

VM-Entry

VMM在机器加电引导后,会进行类似操作系统一样的初始化工作,并在准备就绪时通过VMXON指令进入根模式。在创建客户机时,VMM会通过VMLAUNCH或VMRESUME指令切换到非根模式运行客户机,客户机引起VM-Exit后又切换回根模式运行VMM。

VM-Entry的具体行为由VM-Entry控制域规定:

当CPU执行VMLAUNCH/VMRESUME时:

  1. 执行基本的检查确保VM-Entry可以开始
  2. 对VMCS中的宿主机状态域的有效性进行检查,以确保下一次VM-Exit发生时可以正确地从客户机环境切换到VMM环境
  3. 检查VMCS中客户机状态域地有效性;根据VMCS中客户状态域区域来装载处理器的状态
  4. 根据VMCS中VM-Entry事件注入控制的配置,可能需要注入一个事件到客户机中

VM-Exit

引发VM-Exit的原因有很多,例如在非根模式执行了敏感指令、发生了中断等。

非根模式下的敏感指令

敏感指令如果运行在VMX非根模式,其行为可能会发生改变:

  1. 行为不变化但不引起VM-Exit:虽然时敏感指令,但它不需要VMM截获和模拟,例如SYSENTER指令
  2. 行为变化,产生VM-EXIT:典型的需要截获并模拟的指令
  3. 行为变化,产生VM-EXIT可控:这类敏感指令是否产生VM-EXIT可以通过VM-EXECUTION域来控制

VM-EXECUTION控制域

主要控制三个方面:

  1. 控制某条敏感指令是否产生VM-EXIT,如果产生则由VMM模拟该指令
  2. 在某些敏感指令不产生VM-EXIT时,控制该指令的行为
  3. 异常和中断是否产生VM-EXIT

VM-EXECUTION控制域有多种分类,详情见书。

VM-Exit控制域

VM-Exit控制域规定了VM-Exit发生时CPU的行为:

VM-Exit信息域

VM-Exit信息域提供了VM-Exit时的相关信息。

  1. 基本的VM-Exit信息
  2. 事件触发导致的VM-Exit的信息。事件是指外部中断、异常和NMI。
  3. 事件注入导致的VM-Exit信息。一个事件在注入客户机时,可能由于某种原因暂时不能成功,而触发VM-Exit。
  4. 执行指令导致的VM-Exit的信息。

VM-Exit的过程

  1. CPU首先将此次VM-Exit的原因信息记录到VMCS相应的信息域中,VM-Entry interruption-informatin字段的有效位被清零
  2. CPU状态被保存到VMCS客户机状态域。根据设置,CPU也可能将客户机的MSR保存到VM-Exit MSR-store区域。
  3. 根据VMCS中宿主机状态域和VM-Exit控制域中的设置,将宿主机状态加载到CPU相应寄存器。CPU也可能根据VM-Exit MSR-store区域来加载VMM的MSR
  4. CPU由非根模式切换到了根模式,从宿主机状态域中CS:RIP指定的VM-Exit入口函数开始执行。

CPU虚拟化的实现

Overview

硬件虚拟化使用VCPU描述符来描述虚拟CPU。VCPU描述符类似操作系统中进程描述符,本质是一个结构体,通常由下列几个部分组成:

  1. VCPU标识信息
  2. 虚拟寄存器信息
  3. VCPU状态信息
  4. 额外寄存器/部件信息
  5. 其他信息

Intel VT-x情况下的VCPU可以划分为两个部分,一个是以VMCS为主由硬件使用和更新的部分,这主要是虚拟寄存器;一个是除VMCS之外,由VMM使用和更新的部分,主要指VMCS以外的部分。

VCPU的创建

创建VCPU实际上就是创建VCPU描述符,本质上就是为VCPU描述符结构体分配一个空间。VCPU初始化过程:

  1. 分配VCPU标识
  2. 初始化虚拟寄存器组
  3. 初始化VCPU状态信息
  4. 初始化额外条件
  5. 初始化其它信息

VCPU的运行

  1. 上下文切换

  1. VCPU的硬件优化,两种优化方法:

VCPU的退出

对VCPU退出的处理是VMM进行CPU虚拟化的核心:

  1. 发生VM-Exit,CPU自动进行一部分上下文的切换
  2. 当CPU切换到根模式开始运行VM-Exit的处理函数后,进行另一部分上下文的切换工作

VCPU退出的原因

  1. 访问了特权资源,对CR和MSR寄存器的访问都属于这一类
  2. 客户机执行的指令引发了异常,例如缺页错误
  3. 发生了中断

VCPU的再运行

VMM在处理完VCPU的退出后,会再次运行VCPU:

  1. 如果VCPU继续在相同的物理CPU上运行,可以用VMRESUME来实现VM-Entry
  2. 如果由于某种原因,VCPU被调度程序迁移到了另外一个物理CPU上,那么VMM需要完成:

内存虚拟化

EPT

参见 内存虚拟化硬件基础——EPT_享乐主的博客-CSDN博客

EPT原理

硬件层面引入EPTP寄存器,来指向EPT页表基地址。Guest运行时,Guest页表被载入PDBR,而 EPT 页表被载入专门的EPT 页表指针寄存器 EPTP。

GVA->GPA的转换依然是通过查找原来页表完成,而GPA->HPA的转换则通过查找EPT来实现,每个guest VM有一个由VMM维护的EPT。

当Guest中进程访问GVA时,CPU首先就要通过PDBR寄存器去找页目录,但是PDBR中存储的地址是GPA,所以要到EPT中进行GPA->HPA的转换,这个转换过程和物理MMU的工作流程相同。

找到了页目录的HPA基地址,再通过GVA中的Directory offset段,就找到页表的VGA了,这个页表VGA再去EPT中进行GPA->HPA的转换,就找到页表VGA的HPA了。

重复上述过程,依次查找各级页表,最终获得该GVA对应的HPA。如果是三级或者四级页表,也是同样的原理。

这里假设客户机页表和EPT页表都是4级页表,CPU完成一次地址转换的基本过程如下:

  1. CPU会查找Guest CR3指向的L4页表。由于Guest CR3给出的是GPA,因此CPU需要通过EPT页表来实现Guest CR3 GPA->HPA的转换。CPU首先会查看硬件的EPT TLB,如果没有相应的转换CPU会进一步查证EPT页表,如果还没有,CPU则会抛出EPT Violation异常由VMM处理
  2. 获得L4页表地址后,CPU根据GVA和L4页表项的内容来获取L3页表项的GPA,如果L4页表中GVA对应的页表项显示为缺页,那么CPU产生Page Fault,直接交由Guest Kernel处理。注意,这里不会产生VM-Exit。获得L3页表项的GPA后,CPU同样需要通过查询EPT页表来实现L3 GPA——>HPA的转换,过程和上面一样
  3. CPU最终会获得GVA对应的GPA,然后通过查询EPT页表获得HPA

从上面的过程可以看出,CPU需要5次查询EPT页表,每次查询都需要4次访存,因此最坏情况需要20次访存。

EPT的使用

  1. 在VMCS相应字段写入指明打开EPT功能
  2. 设置好EPT页表。
  3. 当CPU开始使用EPT时,VMM还需要处理EPT Violation:

VPID

每次VM-Entry和VM-Exit时,CPU会强制TLB失效。因为CPU无法区分一个TLB项时属于VMM还是某一特定的虚拟机虚拟处理器。

VPID是一种硬件级的对TLB资源管理的优化,通过在硬件上为每个TLB项增加一个标志,来标识不同的虚拟处理器地址空间,从而区分开来VMM以及不同虚拟机的不同虚拟处理器的TLB。这样可以避免在VM-Entry和VM-Exit时,使全部的TLB失效。

请 Ta 喝咖啡 ☕️